package com.jinhua.mybatis.handler;



import com.jinhua.mybatis.annotation.Column;
import com.jinhua.mybatis.annotation.Id;
import com.jinhua.mybatis.annotation.Table;
import com.jinhua.mybatis.annotation.Transient;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.taskdefs.SQLExec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.LockSupport;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据的自动生成的迭代
 */

public class DbHandler {
    private static final int NOT_DEFAULT_LENGTH = -1;
    private static final int NOT_ASSIGN_LENGTH = -2;
    private static final int NOT_PRECISION_LENGTH = -3;
    private static final int NOT_DATE_DEFAULT_LENGTH = -4;
    private static final int SMALLINT_MAX_LENGTH = 5;
    private static final int TINYINT_MAX_LENGTH = 3;
    private static final int DEFAULT_COLUMN_LENGTH = SMALLINT_MAX_LENGTH + TINYINT_MAX_LENGTH;
    private static final String COLUMN_SPLIT = ",";
    private static final String DEFAULT_FILE_PREFIX = "file:";
    private static final String PACKAGE_BASE_SPLIT = ",";
    private static final String CLASS_INTERVAL_SYMBOL = ".";
    private static final String PATH_DIAGONAL_SYMBOL = "/";
    private static final String BLANK_REGULAR_SYMBOL = "\\s+";
    private static final String BLANK_SYMBOL = " ";
    private static final String UTF8_CHARSET = "UTF-8";
    private static final String JAR_NAME_SYMBOL = "jar";
    private static final String FILE_NAME_SYMBOL = "file";
    private static final String CHARACTER_PATTERN = "CHARACTER SET \\w+";
    private static final String EMPTY = "";
    private static Boolean LOADING = Boolean.TRUE;
    private final int ERROR_HIGH_GRADE = 4;
    private final int ERROR_JUNIOR_GRADE = 1;
    private final int MAX_STRING_LENGTH = 21845;
    private final char UNDERLINE = '_';
    private final String tmpFilePath = System.getProperty("java.io.tmpdir");
    private final long DEFAULT_INVALID_TIME = 1000 * 60 * 60 * 24;
    private final Pattern javaFieldPattern = Pattern.compile("java\\.[a-zA-Z]+\\.[a-zA-Z]+");
    private final String DATASOURCE_ERROR_MSG = "dataSource was not empty";
    private final String CONNECT_ERROR_MSG = "connect was not empty";
    private final String PACKAGEBASE_ERROR_MSG = "packageBase was not empty";
    private final String ID_ERROR_MSG = "@Id annotation was not empty";
    private final String DB_NAME = "db.sql";

    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
    private final Logger log = LoggerFactory.getLogger(DbHandler.class);
    private Boolean isCamel = Boolean.TRUE;
    private Boolean showSql = Boolean.TRUE;
    private Boolean alwaysInit = Boolean.TRUE;
    private Boolean delAndNewTables = Boolean.FALSE;
    private DataSource dataSource;
    private Connection conn;
    /**
     * 包的路径 多个包用,分隔开
     */
    private String packageBase;
    private Thread generateFile;

    public static void getAllClass(String packageBase, Set<Class<?>> classNameList) throws IOException, ClassNotFoundException {
        String[] packageToScannerArr;
        if (packageBase.contains(PACKAGE_BASE_SPLIT)) {
            packageToScannerArr = packageBase.split(PACKAGE_BASE_SPLIT);
        } else {
            packageToScannerArr = new String[]{
                    packageBase
            };
        }
        for (String packName : packageToScannerArr) {
            String pkgPath = packName.replace(CLASS_INTERVAL_SYMBOL, PATH_DIAGONAL_SYMBOL);


            Enumeration<URL> resources = null;

            try {
                resources = DbHandler.class.getClassLoader().getResources(pkgPath);
            } catch (IOException ex) {

            }
            if (resources == null) {
                System.err.printf("packageName %s was incorrect package %n", packName);
                continue;
            }
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();

                if (url == null) {
                    System.err.printf("packageName %s was not exist %n", packName);
                    continue;
                }
                String protocol = url.getProtocol();
                //jar包
                if (JAR_NAME_SYMBOL.equals(protocol)) {
                    findClassesByJar(pkgPath, ((JarURLConnection) url.openConnection()).getJarFile(), classNameList);
                } else if (FILE_NAME_SYMBOL.equals(protocol)) {
                    findClassesByFile(packName, URLDecoder.decode(url.getFile(), UTF8_CHARSET), classNameList);
                } else {
                    System.err.printf("packageName  %s has  incorrect format  %n", packName);

                }

            }

        }


    }

    /**
     * 根据包名添加符合的类
     *
     * @param pkgPath       包的路径
     * @param jar           jar文件流
     * @param classNameList 需要添加的集合
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void findClassesByJar(String pkgPath, JarFile jar, Set<Class<?>> classNameList) throws IOException,
            ClassNotFoundException {


        Enumeration<JarEntry> entry = jar.entries();

        JarEntry jarEntry;
        String name, className;
        Class<?> claze;

        while (entry.hasMoreElements()) {

            jarEntry = entry.nextElement();

            name = jarEntry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }

            if (jarEntry.isDirectory() || !name.startsWith(pkgPath) || !name.endsWith(".class")) {
                continue;
            }
            //如果是一个.class文件 而且不是目录
            // 去掉后面的".class" 获取真正的类名
            className = name.substring(0, name.length() - 6);
            //加载类
            claze = Class.forName(className.replace(PATH_DIAGONAL_SYMBOL, CLASS_INTERVAL_SYMBOL));
            // 添加到集合中去
            if (claze != null) {
                classNameList.add(claze);
            }
        }


    }


    /**
     * 根据包名和路径添加文件形式的类
     *
     * @param pkgName       包名
     * @param pkgPath       包的路径
     * @param classNameList 添加的集合
     */
    private static void findClassesByFile(String pkgName, String pkgPath, Set<Class<?>> classNameList) throws MalformedURLException,
            ClassNotFoundException {

        //获取不是jar内的包名
        File files = new File(pkgPath);
        if (files == null || !files.exists() || !files.isDirectory()) {
            throw new RuntimeException(String.format("packageName %s was not directory %n", pkgName));
        }

        File[] fileArr = files.listFiles(pathname -> pathname.isDirectory() || pathname.getName().endsWith(".class"));

        if (fileArr == null || fileArr.length == 0) {
            return;
        }

        //开头的包名
        String startClassName = pkgName.split(PATH_DIAGONAL_SYMBOL)[0];

        URLClassLoader urlClassLoader;
        String[] classNameArr;
        StringBuilder builder;

        for (File childFile : fileArr) {

            // 如果是目录 则继续扫描
            if (childFile.isDirectory()) {

                findClassesByFile(pkgName + CLASS_INTERVAL_SYMBOL + childFile.getName(),
                        pkgPath + PATH_DIAGONAL_SYMBOL + childFile.getName(), classNameList);
                continue;
            }

            classNameArr = childFile.getAbsolutePath().split(startClassName + "\\\\");
            urlClassLoader = new URLClassLoader(new URL[]{
                    new URL(DEFAULT_FILE_PREFIX + classNameArr[0])
            });
            builder = new StringBuilder();
            builder.append(startClassName);
            builder.append(CLASS_INTERVAL_SYMBOL);
            if (classNameArr.length == 1) {
                String[] _allClassPath = classNameArr[0].split(File.separatorChar == '\\' ? "\\\\" : File.separator);
                builder.append(_allClassPath[_allClassPath.length - 1]);
            } else {

                builder.append(classNameArr[1].replaceAll("\\\\", CLASS_INTERVAL_SYMBOL));
            }

            //去除.class
            classNameList.add(urlClassLoader.loadClass(builder.substring(0, builder.length() - 6)));

        }

    }


    /**
     * 根据字符串获取java的说明
     *
     * @param text 字符串内容
     * @return
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     */
    private static String getJavaDocExplain(String text) {
        Pattern pattern = Pattern.compile("(.*)\\{@(link|linkplain)(.*)}");
        Matcher matcher = pattern.matcher(text);

        try {
            if (matcher.find()) {
                StringBuilder enumsLinkBuilder = new StringBuilder();
                Class<?> findClass = Class.forName(matcher.group(3).trim());

                if (findClass.isEnum()) {

                    //枚举
                    Object[] enumConstants = findClass.getEnumConstants();
                    for (Object enums : enumConstants) {
                        StringBuilder enumsFileBuilder = new StringBuilder();
                        for (Field field : enums.getClass().getDeclaredFields()) {
                            if (!(field.isEnumConstant() || field.getName().equals("$VALUES"))) {
                                field.setAccessible(true);
                                if (enumsFileBuilder.length() != 0) {
                                    enumsFileBuilder.append(":");
                                }
                                enumsFileBuilder.append(field.get(enums));

                            }


                        }
                        if (enumsFileBuilder.length() == 0) {
                            continue;
                        }
                        //如果没有多个构造参数说明，那么把枚举的定义加上
                        if (!enumsFileBuilder.toString().contains(":")) {
                            enumsFileBuilder.insert(0, enums + ":");
                        }
                        if (enumsLinkBuilder.length() != 0) {
                            enumsLinkBuilder.append(",");
                        }

                        enumsLinkBuilder.append(enumsFileBuilder);


                    }
                } else {

                    for (Field field : findClass.getDeclaredFields()) {

                        if (Modifier.isStatic(field.getModifiers())) {
                            field.setAccessible(true);
                            if (enumsLinkBuilder.length() != 0) {
                                enumsLinkBuilder.append(",");
                            }
                            enumsLinkBuilder.append(field.getName());
                            enumsLinkBuilder.append(":");
                            enumsLinkBuilder.append(field.get(findClass));


                        }
                    }


                }
                enumsLinkBuilder.insert(0, matcher.group(1));
                return enumsLinkBuilder.toString();
            }
        } catch (Exception e) {

        }
        return text;

    }


    /**
     * 初始化数据
     *
     * @throws Exception
     */
    public void init() {
        executorService.execute(() -> {
            try {
                loadData();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }

    private void loadData() throws Exception {
        if (LOADING) {
            synchronized (this) {
                if (!alwaysInit) {
                    log.info("本次不初始化实体类和表格映射");
                    return;
                }
                createSql();
                executeSql();
                if (generateFile != null) {
                    LockSupport.unpark(generateFile);
                } else {
                    try {
                        closeConnect(conn);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }

                }
                LOADING = false;
            }
        }

    }

    /**
     * 生成新的sql语句
     *
     * @param file sql存储文件
     * @param list 扫描类的list
     * @throws Exception
     */
    private void autoNewSql(File file, Set<Class<?>> list) throws Exception {
        if (list == null || list.isEmpty() || file == null) {
            return;
        }

        StringBuffer sql = new StringBuffer();

        list.forEach((Class<?> x) -> {

            try {
                x = Class.forName(x.getName());
            } catch (ClassNotFoundException e1) {

            }
            Table table = x.getAnnotation(Table.class);
            if (table != null) {

                Connection connect;
                PreparedStatement statement;

                Boolean isCreateSqlFlag = Boolean.TRUE;
                try {
                    connect = getConnect(dataSource);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                if (sql.length() > 0) {
                    sql.append("\n");
                }
                String tableCheckSql = "show tables like '%s'";
                if (delAndNewTables) {
                    tableCheckSql = "drop table `%s`";
                }

                try {
                    statement = connect.prepareStatement(String.format(tableCheckSql, getTableName(x)));
                    Boolean resultCheck;
                    if (delAndNewTables) {
                        resultCheck = statement.execute();
                    } else {
                        resultCheck = statement.executeQuery().next();
                    }
                    if (resultCheck) {
                        if (delAndNewTables) {
                            //delAndNewTables为false
                            isCreateSqlFlag = Boolean.TRUE;
                        } else {
                            //delAndNewTables为false
                            isCreateSqlFlag = Boolean.FALSE;
                        }
                    }
                } catch (SQLException e) {
                    if (delAndNewTables) {
                        //delAndNewTables 为true
                        isCreateSqlFlag = Boolean.TRUE;
                    } else {
                        throw new RuntimeException(e);
                    }
                }


                if (isCreateSqlFlag) {
                    sql.append("create table  ");


                    sql.append("`");
                    sql.append(getTableName(x));
                    sql.append("` ( ");

                }
                //判断是否修改和添加表注释
                try {

                    if (!table.commit().isEmpty()) {

                        StringBuilder tableCommitBuild = new StringBuilder();
                        tableCommitBuild.append("select table_comment from information_schema.tables where table_schema = '");
                        tableCommitBuild.append(conn.getCatalog());
                        tableCommitBuild.append("' and table_name ='");
                        tableCommitBuild.append(getTableName(x));
                        tableCommitBuild.append("'");
                        ResultSet commitResult = conn.prepareStatement(tableCommitBuild.toString()).executeQuery();
                        String tableCommit = "";
                        while (commitResult.next()) {
                            tableCommit = commitResult.getString(1);
                        }
                        if (!tableCommit.equals(table.commit())) {
                            conn.prepareStatement(String.format("alter table %s comment '%s';", getTableName(x), table.commit())).execute();
                        }
                    }
                } catch (SQLException e) {

                    if (!e.getMessage().endsWith("doesn't exist")) {
//                        throw new RuntimeException(e);
                    }
                }


                List<Field> fields = getAllFields(x);
                String id = "";

                StringBuilder sqlBuild;
                StringBuilder commSql;
                StringBuilder updateSql = new StringBuilder();
                for (Field field : fields) {

                    boolean isJavaFieldMatch = !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()))
                            && javaFieldPattern.matcher(field.getGenericType().getTypeName()).matches();
                    if (!isJavaFieldMatch) {
                        continue;
                    }

                    if (field.getAnnotation(Transient.class) != null) {
                        continue;
                    }
                    String especialColumnName;

                    if (isCamel) {
                        especialColumnName = camel2Underline(field.getName());
                    } else {
                        especialColumnName = field.getName();
                    }

                    commSql = new StringBuilder();
                    String type = field.getType().getName();
                    if (field.getAnnotation(Id.class) != null) {

                        id = especialColumnName;

                        if (isCreateSqlFlag) {
                            sql.append(TableAttribute.getIdCommit(id, type));
                        }
                        continue;
                    }
                    Column column = field.getAnnotation(Column.class);
                    if (column != null && !column.value().isEmpty()) {
                        especialColumnName = column.value();
                    }
                    if (isCreateSqlFlag) {
                        sql.append("`");
                        sql.append(especialColumnName);
                        sql.append("` ");
                    }
                    commSql.append(TableAttribute.getColumn(type, column == null ? DEFAULT_COLUMN_LENGTH : column.length()));
                    commSql.append("(");

                    if (column != null) {
                        if (TableAttribute.getLength(type) == NOT_DEFAULT_LENGTH) {
                            commSql.delete(commSql.length() - 1, commSql.length());

                            //添加字符集
                            if (!column.charset().isEmpty()) {
                                commSql.append(" CHARACTER SET ");
                                commSql.append(column.charset());
                            }


                            //添加默认值
                            commSql.append(!column.defaultVal().isEmpty() ? TableAttribute.getDefaultValue(column.defaultVal(), field) :
                                    column.isNUll() ? TableAttribute.getDefaultNull() : TableAttribute.getNotNull());


                        } else if (TableAttribute.getLength(type) == NOT_DATE_DEFAULT_LENGTH) {

                            commSql.delete(commSql.length() - 1, commSql.length());

                            String dateDefaultVal = column.defaultVal().trim();
                            if (!dateDefaultVal.isEmpty()) {
                                commSql.append(" NULL DEFAULT ");

                                if ("CURRENT_TIMESTAMP".equals(dateDefaultVal)) {
                                    commSql.append(dateDefaultVal);
                                } else {
                                    commSql.append("'");
                                    commSql.append(dateDefaultVal);
                                    commSql.append("'");

                                }

                            } else {
                                DateGeneStrategy dateGeneStrategy = column.dateGeneStrategy();
                                if (dateGeneStrategy != DateGeneStrategy.DEFAULT) {
                                    //判断是否有默认时间
                                    commSql.append(dateGeneStrategy.getDefaultValue());
                                } else {
                                    commSql.append(" DEFAULT null ");

                                }

                            }


                        } else if (TableAttribute.getLength(type) == NOT_ASSIGN_LENGTH) {
                            commSql.delete(commSql.length() - 1, commSql.length());

                            DateGeneStrategy dateGeneStrategy = column.dateGeneStrategy();
                            if (dateGeneStrategy != DateGeneStrategy.DEFAULT) {
                                //判断是否有默认时间
                                commSql.append(dateGeneStrategy.getDefaultValue());
                            } else {
                                throw new RuntimeException(System.err.append(String.format("className %s 的属性 %s 为TimeStamp 必须有默认值", x.getName(), field.getName())).toString());
                            }

                        } else {
                            //精度计算的时候 (M,N) M需要大于N这边默认如果不大于则加上N的精度
                            commSql.append(column.length() == 0 ? TableAttribute.getLength(type) :
                                    column.length() > column.precision() ?
                                            column.length() : column.length() + column.precision());
                            commSql.append(TableAttribute.getPrecision(type) == NOT_PRECISION_LENGTH ? "" :
                                    "," + column.precision());
                            commSql.append(") ");


                            //查看是否是字符串长度是否超过最大长度21845
                            TableAttribute tableAttribute = TableAttribute.getTableAttribute(type);
                            if (tableAttribute != null && TableAttribute.STRING == tableAttribute && column.length() > MAX_STRING_LENGTH) {
                                //大于最大长度最大长度进行更改类型
                                commSql.delete(0, commSql.length() - 1);
                                commSql.append("text");
                            }

                            //添加字符集
                            if (!column.charset().isEmpty()) {
                                commSql.append(" CHARACTER SET ");
                                commSql.append(column.charset());
                            }


                            String _commitDefaultVal = column.defaultVal();
                            //纠正用户写的默认值跟数据库不一致导致更新数据
                            if (column.precision() != 0 && _commitDefaultVal.startsWith("0")) {
                                StringBuilder commitDefaultValBuild = new StringBuilder("0.");
                                for (int i = 0; i < column.precision(); i++) {
                                    commitDefaultValBuild.append("0");
                                }
                                _commitDefaultVal = commitDefaultValBuild.toString();
                            }
                            //添加默认值
                            commSql.append(!_commitDefaultVal.isEmpty() ? TableAttribute.getDefaultValue(_commitDefaultVal, field) :
                                    column.isNUll() ? TableAttribute.getDefaultNull() : TableAttribute.getNotNull());


                        }


                        //添加注释
                        commSql.append(TableAttribute.getCommit(column.commit()));


                    } else {

                        if (TableAttribute.getLength(type) == NOT_DEFAULT_LENGTH) {
                            commSql.delete(commSql.length() - 1, commSql.length());

                            commSql.append(TableAttribute.getDefaultNull());


                        } else if (TableAttribute.getLength(type) == NOT_DATE_DEFAULT_LENGTH) {
                            commSql.delete(commSql.length() - 1, commSql.length());
                            commSql.append(TableAttribute.getNull());


                        } else if (TableAttribute.getLength(type) == NOT_ASSIGN_LENGTH) {
                            commSql.delete(commSql.length() - 1, commSql.length());

                            commSql.append(TableAttribute.getNull());

                        } else {
                            commSql.append(TableAttribute.getLength(type));
                            int precision = TableAttribute.getPrecision(type);
                            commSql.append(precision == NOT_PRECISION_LENGTH ? "" : "," + precision);
                            commSql.append(") ");
                            commSql.append(TableAttribute.getDefaultNull());
                        }


                        commSql.append(TableAttribute.getDefaultCommit());

                    }

                    if (isCreateSqlFlag) {

                        //分别赋予sql
                        sql.append(commSql.toString());

                    }

                    try {


                        sqlBuild = new StringBuilder("SHOW FULL COLUMNS FROM ");
                        sqlBuild.append(getTableName(x));
                        sqlBuild.append(" WHERE FIELD='");
                        sqlBuild.append(especialColumnName);
                        sqlBuild.append("'");

                        statement = connect.prepareStatement(sqlBuild.toString());
                        ResultSet resultSet = statement.executeQuery();
                        String commitSqlSuffix = commSql.delete(commSql.length() - 1, commSql.length()).toString();

                        if (resultSet.next()) {
                            if (column == null) {
                                continue;
                            }

                            String lengthInfo = resultSet.getString(2);
                            String originCharSet = resultSet.getString(3);
                            String isNotNull = resultSet.getString(4);
                            String defaultInfo = resultSet.getString(6);
                            String commitInfo = resultSet.getString(9);
                            StringBuilder originSqlDesBuild = new StringBuilder();
                            originSqlDesBuild.append(lengthInfo);
                            originSqlDesBuild.append(" ");
                            //忽略null的情形
                            if ("no".equalsIgnoreCase(isNotNull)) {
                                originSqlDesBuild.append("NOT NULL ");

                            } else {
                                if (defaultInfo == null) {
                                    originSqlDesBuild.append(" DEFAULT ");
                                    originSqlDesBuild.append(defaultInfo);
                                    originSqlDesBuild.append(" ");

                                } else {
                                    originSqlDesBuild.append("DEFAULT ");
                                    originSqlDesBuild.append("'");
                                    originSqlDesBuild.append(defaultInfo);
                                    originSqlDesBuild.append("'");
                                }

                            }
                            originSqlDesBuild.append(" COMMENT '");
                            originSqlDesBuild.append(commitInfo);
                            originSqlDesBuild.append("'");


                            String originSql = originSqlDesBuild.toString();


                            //检测是否是boolean类型
                            boolean booleanTypeFlag = (originSql.contains("bit(1) DEFAULT 'b'0''") && (
                                    commitSqlSuffix.trim().contains("bit(1) DEFAULT false")
                                            || commitSqlSuffix.trim().contains("bit(1) DEFAULT 0"))) || (
                                    originSql.contains("bit(1) DEFAULT 'b'1''") && (
                                            commitSqlSuffix.trim().contains("bit(1) DEFAULT true")
                                                    || commitSqlSuffix.trim().contains("bit(1) DEFAULT 1"))
                            );

                            //检测时间
                            Boolean checkDateDefaultFlag = null;

                            if (commitSqlSuffix.startsWith("TIMESTAMP") || commitSqlSuffix.startsWith("datetime") || commitSqlSuffix.startsWith("date") || commitSqlSuffix.startsWith("time")) {
                                //比较特殊的时间
                                checkDateDefaultFlag = originSql.trim().replaceAll(BLANK_REGULAR_SYMBOL, BLANK_SYMBOL
                                ).replaceAll("'CURRENT_TIMESTAMP'", "CURRENT_TIMESTAMP").equalsIgnoreCase(commitSqlSuffix.trim()
                                        .replaceAll(BLANK_REGULAR_SYMBOL, BLANK_SYMBOL).trim().replaceAll("NULL ", EMPTY).
                                                replaceAll("ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP", "NULL").
                                                replaceAll(CHARACTER_PATTERN, EMPTY));
                            }

                            //字符集
                            boolean charSetFlag = (originCharSet != null && !originCharSet.isEmpty()
                                    && !column.charset().isEmpty()) && !originCharSet.split(String.valueOf(UNDERLINE))[0].equalsIgnoreCase(column.charset());


                            //加入高版本的mysql自动忽略int()后面的长度或者tinyint等等数量后面的长度
                            boolean higherMysqlVersionNumberIgnoreFlag =
                                    TableAttribute.conformDigit(originSql, commitSqlSuffix);

                            //字符集|| 时间
                            if ((charSetFlag || (checkDateDefaultFlag != null && !checkDateDefaultFlag) ||
                                    //排出时间的判断
                                    (checkDateDefaultFlag == null &&
                                            !originSql.trim().replaceAll(BLANK_REGULAR_SYMBOL, BLANK_SYMBOL).
                                                    equalsIgnoreCase(commitSqlSuffix.trim().replaceAll(CHARACTER_PATTERN, EMPTY).
                                                            replaceAll(BLANK_REGULAR_SYMBOL, BLANK_SYMBOL))
                                            && !booleanTypeFlag)) &&
                                    //排除高版本的mysql的数字单位的位数
                                    !higherMysqlVersionNumberIgnoreFlag
                            ) {


                                updateSql.append("alter table ");
                                updateSql.append(getTableName(x));
                                updateSql.append(" MODIFY COLUMN `");
                                updateSql.append(especialColumnName);
                                updateSql.append("` ");
                                updateSql.append(commitSqlSuffix);
                                updateSql.append("; \n");

                            }

                        } else {

                            updateSql.append("alter table  ");
                            updateSql.append(getTableName(x));
                            updateSql.append(" add ");
                            updateSql.append(especialColumnName);
                            updateSql.append(" ");
                            updateSql.append(commitSqlSuffix);
                            updateSql.append("; \n");


                        }

                    } catch (SQLException e) {

                        if (!e.getMessage().endsWith("doesn't exist")) {
//                            throw new RuntimeException(e);
                        }
                    }


                }

                if (id.isEmpty()) {
                    throw new RuntimeException(System.out.append(x.getName()).append(" ").append(ID_ERROR_MSG).append(" \n").toString());
                }


                if (isCreateSqlFlag) {
                    sql.append(TableAttribute.getPrimaryId(id, table.charset(), table.commit()));
                }
                sql.append("\n");
                sql.append(updateSql);


            }
        });


        if (sql.length() != 0) {

            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true));
            osw.write(sql.toString());
            osw.close();

        }


    }

    /**
     * 获取表名
     *
     * @param tableClass 实体类
     * @return
     */
    private String getTableName(Class<?> tableClass) {

        String tableName;

        Table annotation = tableClass.getAnnotation(Table.class);
        String prefix = (annotation == null || annotation.prefix().trim().isEmpty()) ? "" : annotation.prefix().trim() + "_";

        if (annotation != null && !annotation.value().isEmpty()) {

            tableName = annotation.value().trim();


        } else {
            tableName = lowerFirstCase(tableClass.getSimpleName());
            if (isCamel) {
                tableName = camel2Underline(tableName);
            }

        }
        return prefix + tableName;
    }

    /**
     * 根据类获取所有的私有属性
     *
     * @param aClass 需要获取的类
     * @return
     */
    private List<Field> getAllFields(Class<?> aClass) {
        List<Field> fieldList = new ArrayList<>();

        Field[] fieldArr = aClass.getDeclaredFields();
        for (Field field : fieldArr) {
            if (Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            fieldList.add(field);

        }

        while (!aClass.getCanonicalName().equals(Object.class.getCanonicalName())) {
            aClass = aClass.getSuperclass();
            fieldArr = aClass.getDeclaredFields();
            for (Field field : fieldArr) {
                if (Modifier.isProtected(field.getModifiers()) ||
                        Modifier.isProtected(field.getModifiers())
                                && !Modifier.isFinal(field.getModifiers())) {

                    if (field.getAnnotation(Id.class) != null) {
                        if (fieldList.size() > 0) {
                            fieldList.add(fieldList.get(0));
                            fieldList.set(0, field);
                            continue;
                        }

                    }

                    fieldList.add(field);
                }


            }


        }

        return fieldList;
    }

    /**
     * 首字母小写
     *
     * @param name 需要首字母小写的名称
     * @return
     */
    private String lowerFirstCase(String name) {
        char[] chars = name.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 生成sql
     */
    private void createSql() throws Exception {
        if (dataSource == null) {
            throw new RuntimeException(System.out.append(DATASOURCE_ERROR_MSG).append(" \n").toString());
        }
        Connection connect = getConnect(dataSource);

        if (connect == null) {
            throw new RuntimeException(System.out.append(CONNECT_ERROR_MSG).append(" \n").toString());
        }
        if (packageBase == null || packageBase.isEmpty()) {
            throw new RuntimeException(System.out.append(PACKAGEBASE_ERROR_MSG).append(" \n").toString());
        }
        log.info("开始扫描{}包下面的表格实体类", packageBase);
        Set<Class<?>> classNameList = new HashSet<>();

        //加载符合的class
        getAllClass(packageBase, classNameList);

        //生产sql文件
        File file = new File(tmpFilePath, DB_NAME);

        if (file.exists() || file.length() == 0 || System.currentTimeMillis() - file.lastModified() > DEFAULT_INVALID_TIME) {
            file.delete();
        }

        file.createNewFile();
        autoNewSql(file, classNameList);

        log.info("结束扫描{}包下面的表格实体类", packageBase);
    }

    /**
     * 执行sql
     */
    private void executeSql() throws NoSuchFieldException, SQLException, IllegalAccessException {

        log.info("开始进行数据库导入操作");

        DefaultLogger logger = new DefaultLogger();
        //设定输入流
        logger.setErrorPrintStream(System.err);
        logger.setOutputPrintStream(System.out);
        //设定错误级别
        logger.setMessageOutputLevel(showSql ? ERROR_HIGH_GRADE : ERROR_JUNIOR_GRADE);

        Project project = new Project();
        //绑定日志
        project.addBuildListener(logger);

        //创建目标文件
        Target target = new Target();
        //创建工作目标
        SQLExec task = new SQLExec();

        //利用反射创建connection
        Field conn = task.getClass().getDeclaredField("conn");
        conn.setAccessible(Boolean.TRUE);
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(Boolean.FALSE);
        conn.set(task, connection);
        //创建执行sql的文件
        task.setSrc(new File(tmpFilePath + File.separator + DB_NAME));
        task.setProject(project);
        task.setTaskName(this.getClass().getSimpleName());

        //目标添加工作
        target.addTask(task);
        target.setProject(project);
        target.execute();

        log.info("进行数据库导入操作success....");


    }

    /**
     * 获取链接
     *
     * @param source
     * @return
     * @throws Exception
     */
    private Connection getConnect(DataSource source) throws Exception {
        if (source == null) {
            return null;
        }
        if (conn == null) {
            conn = source.getConnection();
        }
        return conn;

    }

    /**
     * 驼峰格式字符串转换为下划线格式字符串
     *
     * @param param
     * @return
     */
    private String camel2Underline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 关闭连接
     *
     * @param connection
     * @throws SQLException
     */
    private void closeConnect(Connection connection) throws SQLException {
        if (connection != null && !connection.isClosed()) {
            connection.close();
        }
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setPackageBase(String packageBase) {
        this.packageBase = packageBase;
    }

    public void setShowSql(Boolean showSql) {
        this.showSql = showSql;
    }

    public void setAlwaysInit(Boolean alwaysInit) {
        this.alwaysInit = alwaysInit;
    }

    public void setAlwaysDrop(Boolean delAndNewTables) {
        this.delAndNewTables = delAndNewTables;
    }

    public void setIsCamel(Boolean isCamel) {
        this.isCamel = isCamel;
    }

    /**
     * 生成实体文件
     *
     * @param isModify      是否修正错误表的错误字段
     * @param blurPrefix    去除表的前缀 多个利用,分开
     * @param packageName   需要生成的包名称
     * @param needTableName 需要生成的表的名称,利用*支持模糊搜索 eg: example_table*_name(example_tableA_name,example_table_name皆匹配)
     */
    public void generate(Boolean isModify, String blurPrefix, String packageName, String... needTableName) {

        //另外一个线程，无法获取主线程的内容
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String currentClassName = stackTrace[stackTrace.length - 1].getClassName();
        String currentSourceFileName = currentClassName.replaceAll("\\.",
                File.separator.equalsIgnoreCase("\\") ? "\\\\" : File.separator) + ".java";

        generateFile = new Thread(() -> {

            try {
                LockSupport.park(this);
                GenerateEntity.generateJavaFile(isModify, needTableName, blurPrefix, packageBase, packageName, conn, currentSourceFileName);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        generateFile.start();

    }

    public enum DateGeneStrategy {
        DEFAULT(""), CREATE(" NULL DEFAULT  CURRENT_TIMESTAMP"), UPDATE(" NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP");
        private final String value;

        DateGeneStrategy(String value) {
            this.value = value;
        }

        public String getDefaultValue() {
            return value;
        }

    }

    private enum TableAttribute {
        DATE("java.util.Date", "datetime ", NOT_DATE_DEFAULT_LENGTH, NOT_PRECISION_LENGTH),
        LOCALDATETIME("java.time.LocalDateTime", "datetime ", NOT_DATE_DEFAULT_LENGTH, NOT_PRECISION_LENGTH),
        LOCALDATE("java.time.LocalDate", "date ", NOT_DATE_DEFAULT_LENGTH, NOT_PRECISION_LENGTH),
        LOCALTIME("java.time.LocalTime", "time ", NOT_DATE_DEFAULT_LENGTH, NOT_PRECISION_LENGTH),
        TIMESTAMP("java.sql.Timestamp", "TIMESTAMP", NOT_ASSIGN_LENGTH, NOT_PRECISION_LENGTH),
        LONG("java.lang.Long", "bigint", 11, NOT_PRECISION_LENGTH),
        DOUBLE("java.lang.Double", "double", 11, 0),
        BIGDECIMAL("java.math.BigDecimal", "decimal", 10, 0),
        INTEGER("java.lang.Integer", "tinyint,smallint,int", 11, NOT_PRECISION_LENGTH),
        SHORT("java.lang.Short", "smallint", 4, NOT_PRECISION_LENGTH),
        STRING("java.lang.String", "varchar", 2 << 8, NOT_PRECISION_LENGTH),
        BOOLEAN("java.lang.Boolean", "bit", 1, NOT_PRECISION_LENGTH);
        private final String key;
        private final String columnName;
        private final int value;
        private final int precision;

        TableAttribute(String key, String columnName, int value, int precision) {
            this.key = key;
            this.columnName = columnName;
            this.value = value;
            this.precision = precision;
        }

        public static boolean conformDigit(String oringinSql, String commitSql) {
            //原来单位的与修改单位不一致直接返回
            if (!commitSql.startsWith(oringinSql.split(" ")[0])) {
                return false;
            }
            return Pattern.matches("(tinyint|smallint|bigint|int)\\s+DEFAULT(.*)", oringinSql);
        }

        private static int getLength(String key) {

            if (key == null || key.isEmpty()) {
                return 0;
            }
            TableAttribute[] values = TableAttribute.values();

            for (TableAttribute defaultAttr : values) {

                if (defaultAttr.key.equals(key)) {

                    return defaultAttr.value;
                }
            }

            return 0;
        }

        private static int getPrecision(String key) {

            if (key == null || key.isEmpty()) {
                return NOT_PRECISION_LENGTH;
            }
            TableAttribute[] values = TableAttribute.values();

            for (TableAttribute defaultAttr : values) {

                if (defaultAttr.key.equals(key)) {

                    return defaultAttr.precision;
                }
            }

            return NOT_PRECISION_LENGTH;
        }

        private static TableAttribute getTableAttribute(String key) {
            if (key == null || key.isEmpty()) {
                return null;
            }
            for (TableAttribute attribute : values()) {
                if (attribute.key.equals(key)) {
                    return attribute;
                }
            }
            return null;
        }

        private static String getNotNull() {
            return "NOT NULL ";
        }

        private static String getNull() {
            return " NULL ";
        }

        private static String getDefaultNull() {
            return " DEFAULT NULL ";
        }

        private static String getDefaultCommit() {
            return ",";
        }

        private static String getPrimaryId(String key, String charset, String commit) {
            StringBuilder builder = new StringBuilder("PRIMARY KEY (`");
            builder.append(key);
            builder.append("`) ");
            builder.append(" ) ENGINE=InnoDB DEFAULT CHARSET=");
            builder.append(charset);

            if (!commit.isEmpty()) {
                builder.append(" COMMENT='");
                builder.append(commit);
                builder.append("'");

            }
            builder.append(";");

            return builder.toString();
        }

        private static String getCommit(String value) {
            StringBuilder builder = new StringBuilder(" COMMENT '");
            builder.append(getJavaDocExplain(value));
            builder.append("' ,");
            return builder.toString();
        }

        private static String getIdCommit(String value, String type) {
            StringBuilder builder = new StringBuilder("`");
            builder.append(value);
            builder.append("` ");
            builder.append(getColumn(type, DEFAULT_COLUMN_LENGTH));
            builder.append(" (");
            builder.append(getLength(type));
            builder.append(") ");
            builder.append("NOT NULL  AUTO_INCREMENT , ");
            return builder.toString();
        }

        private static String getColumn(String key, int length) {

            if (key == null || key.isEmpty()) {
                return "";
            }
            TableAttribute[] values = TableAttribute.values();

            for (TableAttribute defaultAttr : values) {
                if (defaultAttr.key.equals(key)) {
                    String columnName = defaultAttr.columnName;
                    if (defaultAttr.columnName.contains(COLUMN_SPLIT)) {

                        String[] _columnArr = defaultAttr.columnName.split(COLUMN_SPLIT);
                        int index;
                        if (length <= TINYINT_MAX_LENGTH) {
                            index = 0;
                        } else if (length <= SMALLINT_MAX_LENGTH) {
                            index = 1;
                        } else {
                            index = _columnArr.length - 1;
                        }
                        columnName = _columnArr[index];
                    }
                    return columnName;

                }
            }

            return "";
        }

        private static String getDefaultValue(String key, Field field) {

            if (field.getType() == Boolean.class) {

                return "DEFAULT " + key + "";
            }
            if (key == null || key.isEmpty()) {
                return "";
            }

            return "DEFAULT '" + key + "'";
        }


    }


}
